#ifndef MATERIALMANAGER_H
#define MATERIALMANAGER_H

#include <grouptreeitem.h>

#include <gfx/enum.h>
#include <gfx/ishader.h>
#include <gfx/idevice.h>


#include <QMap>
#include <QList>
#include <QObject>
#include <QString>
#include <QVector>

class MaterialManager;

class MaterialData : public QObject
{
  Q_OBJECT
private:
  QString       _name;
  QString       _extension;
  Category     _groupItem;

protected:
  MaterialData (const QString& name);
  MaterialData (const MaterialData& other);
public:
  void SetName (const QString& name);
  const QString& GetName () const;

  void SetExtensionName (const QString& name);
  const QString& GetExtensionName () const;

  void SetExportName (const QString& name);
  virtual QString GetExportName() const;
  virtual QString GetFullname () const;

  void SetCategory (const Category& item);
  const Category& GetCategory () const;
  Category& GetCategory ();

  virtual QString GetTypeName () const = 0;

  virtual void Change ();
  virtual void Save ();
  virtual void Move (const Category& group);
  virtual void Remove ();

signals:
  void NameChanged ();
  void Changed ();
  void Removed ();
  void Moved (const Category& from, const Category& to);
};

class Shader : public MaterialData
{  
  friend class MaterialManager;
  Q_OBJECT
public:

  enum Version
  {
    GLSL_110,
    GLSL_120,
    GLSL_130,
    GLSL_140,
    GLSL_150,
    GLSL_330,
    GLSL_400,
    GLSL_410,
    GLSL_420,
    GLSL_COUNT
  };
private:

  QString           _code;
  Version           _version;

  MaterialManager*  _materialManager;
  iShader*          _shader;
  QString GetExtensionName() const;


  QList<QString>					_attributes;
  QList<QString>					_uniforms;

  void ScanText (QList<QString>& list, const QString& keyword);
  void EvalVersion ();
  void ScanText ();


  Shader (MaterialManager* _materialManager, iShader* shader);
public:


  QString GetTypeName () const { return "Shader"; }

  static Shader::Version GetShaderVersionFromString (const QString& name);
  static QString GetShaderVersionString (Shader::Version version);
  static QString GetShaderVersionName (Shader::Version version);

  virtual QString GetFullname () const;

  void SetCode (const QString& code);
  const QString& GetCode () const;

  void SetShaderType (ceShaderType type);
  ceShaderType GetShaderType () const;
  Version GetVersion () const;

  iShader* GetShader ();

  virtual void Save ();
  virtual void Move (const Category& group);
  virtual void Remove ();

  const QList<QString>& GetAttributes () const { return _attributes; }
  const QList<QString>& GetUniforms() const { return _uniforms; }

};


class Texture : public MaterialData
{
  friend class MaterialManager;
  Q_OBJECT
public:
  enum Wrap
  {
		Wrap_Default,
    Wrap_Repeat,
    Wrap_Clamp,
    Wrap_ClampToEdge,
    Wrap_ClampToBorder
  };

  enum Filter
  {
		Filter_Default,
    Filter_Anisotropic,
    Filter_Trilinear,
    Filter_Bilinear,
    Filter_Nearest
  };

	enum MipMap
	{
		MipMap_Default,
		MipMap_On,
    MipMap_Off
	};

private:

  unsigned  static static_idx;
  unsigned  _idx;

  QString   _filename;
  int       _wrap;
  int       _filter;
	int				_mipMap;
  int       _anisotropy;

  iTexture*         _texture;

  MaterialManager*  _materialManager;

  Texture (MaterialManager* materialManager, iTexture* texture);
public:
  QString GetTypeName () const { return "Texture"; }

  Texture (const Texture& other);
  ~Texture ();
  unsigned GetIdx () const;

  iTexture* GetTexture ();
  const iTexture* GetTexture () const;

  void SetFilename(const QString& filename);
  const QString& GetFilename () const;

  void SetWrap (int wrap);
  int GetWrap () const;

  void SetFilter (int filter);
  int GetFilter () const;

  void SetAnisotropy (int anisotrophy);
  int GetAnisotropy () const;

	void SetMipMap (int mipMap);
	int GetMipMap () const;

  void UpdateData ();

  virtual void Save ();
  virtual void Move (const Category& group);
  virtual void Remove ();

};

class Program : public MaterialData
{
  friend class MaterialManager;
public:
  class ShaderProgram
  {
    friend class Program;
  private:
    QList<Shader*>  _shaders;
    iProgram*       _program;
    QString          _name;

    ShaderProgram (iProgram* program);
  public:

    void SetName (const QString& name);
    const QString& GetName () const;

    bool Relink (Shader* shader);

    void AddShader (Shader* shader);
    void RemoveShader (Shader* shader);
    int GetNumberOfShaders () const;
    Shader* GetShader (int idx);

    iProgram* GetProgram () const;

    Shader::Version GetVersion () const;

  };

private:

  MaterialManager*  _materialManager;

  QMap<QString, QString>  _bindingMapping;


  Program (MaterialManager* materialManager);

  QList<ShaderProgram*>  _programs;
public:

  QString GetTypeName() const { return "Program"; }

  ShaderProgram* CreateProgram ();

  int AddProgram (ShaderProgram* program);
  void RemoveProgram (ShaderProgram* program);
  int GetNumberOfPrograms () const;
  ShaderProgram* GetProgram (int idx);
  ShaderProgram* EvalBestProgram ();

  void UpdateBindingValues ();
  void AutoAssignBindings ();
  int GetNumberOfBindings () const;
  const QString& GetBindingKey (int idx) const;
  const QString& GetBindingValue (int idx) const;
  void SetBindingValue (const QString& key, const QString& value);
  void SetBindingValue (int idx, const QString& value);
  void UpdateProgramBindings ();

  void Relink (Shader* shader);

  Program (const Program &other);

  virtual void Save ();
  virtual void Move (const Category& group);
  virtual void Remove ();

};

class Effect : public MaterialData
{
  friend class MaterialManager;
  Q_OBJECT
private:
  struct Stage
  {
    QMap<QString, Program*> programs;
    iEffectProgram*		effect;
  };

	iEffect*					_effect;

  QMap<QString, ceRenderPass> _pass;
  QVector<Stage>  _stages;

  MaterialManager*  _materialManager;

	QString						_solverName;


  Effect (MaterialManager* manager, iEffect* effect);
public:
//  Effect (const Effect& other);
  QString GetTypeName () const { return "Effect"; }

	void SetSolver (const QString& solver);
	const QString& GetSolver () const;

  void ClearStages ();
  void AddStage ();
  bool RemoveStage (unsigned idx);
  unsigned GetNumberOfStages () const;

	iEffect* GetEffect ();
	const iEffect* GetEffect () const;
  iEffectProgram* GetEffect (unsigned stage);
  const iEffectProgram* GetEffect (unsigned stage) const;

  void SetProgram (unsigned stage, const QString& name, Program* program);
  Program* GetProgram (unsigned stage, const QString& name);

  unsigned GetNumberOfPrograms (unsigned stage) const;
  const QString& GetProgramName (unsigned stage, unsigned idx);


  virtual void Save ();
  virtual void Move (const Category& group);
  virtual void Remove ();

};

class Material : public MaterialData
{
  friend class MaterialManager;
private:
  ceMaterial  *_material;

  Effect      *_effect;

  MaterialManager *_materialManager;

  Material (MaterialManager* materialManager, ceMaterial* material);
public:

  QString GetTypeName () const { return "Material"; }

  void SetEffect (Effect* effect);
  Effect* GetEffect ();
  const Effect* GetEffect () const;

  ceMaterial* GetMaterial ();

  virtual void Save ();
  virtual void Move (const Category& group);
  virtual void Remove ();

};

class MaterialManager : public QObject
{
  Q_OBJECT
public:
  enum DataValues
  {
    DV_Textures   = 0x01,
    DV_Shaders    = 0x02,
    DV_Programs   = 0x04,
    DV_Effects    = 0x08,
    DV_Materials  = 0x10,
    DV_All        = 0xff
  };

private:
  QMap<QString, QList<MaterialData*> >  _datas;

  const MaterialData* GetMaterialData (const QList<MaterialData*>& datas, const QString& name) const;
  MaterialData* GetMaterialData (QList<MaterialData*>& datas, const QString& name);

  void UpdatePrograms (Shader* shader);

  bool LoadTextures ();
  void SaveTextures ();
  bool LoadShaders ();
  void SaveShaders ();
  bool LoadPrograms ();
  void SavePrograms ();
  bool LoadEffects ();
  void SaveEffects ();
  bool LoadMaterials ();
  void SaveMaterials ();

public:
  MaterialManager();

  bool CreateDefaultMaterial ();

  bool Load (int values = DV_All);
  void Save (int values = DV_All);

  QList<MaterialData*>& Textures ();
  QList<MaterialData*>& Shaders ();
  QList<MaterialData*>& Programs ();
  QList<MaterialData*>& Effects ();
  QList<MaterialData*>& Materials();
  const QList<MaterialData*>& Textures () const;
  const QList<MaterialData*>& Shaders () const;
  const QList<MaterialData*>& Programs() const;
  const QList<MaterialData*>& Effects() const;
  const QList<MaterialData*>& Materials() const;


  Material* FindMaterial(const ceMaterial* material);
  Effect* FindEffect (const iEffect* effect);
  Texture* FindTexture (const iTexture* texture);


  void RenameMaterialDataGroup (const Category& from, const Category& to);
  void RemoveMaterialDataGroup (const Category& group);

  /**
   * \name Texture handling
   * @{
   */
  bool ContainsTexture (Texture* texture);
  bool AddTexture (Texture* texture);
  bool RemoveTexture (Texture* texture);
  void MoveTexture (Texture* texture);
  bool SaveTexture (Texture* texture);


  unsigned GetNumberOfTextures () const;
  void RemoveTexture (unsigned idx);
  const Texture* GetTexture (const QString& name) const;
  Texture* GetTexture (const QString& name);
  const Texture* GetTexture (unsigned idx) const;
  Texture* GetTexture (unsigned idx);
  Texture* CreateTexture ();
  /**
   * @}
   */

  /**
   * \name Shader handling
   * @{
   */
  bool ContainsShader (Shader* shader);
  bool AddShader (Shader* shader);
  bool RemoveShader (Shader* shader);
  void MoveShader (Shader* shader);
  bool SaveShader (Shader* shader);


  unsigned GetNumberOfShaders () const;
  void RemoveShader (unsigned idx);
  const Shader* GetShader (const QString& name) const;
  Shader* GetShader (const QString& name);
  const Shader* GetShader (unsigned idx) const;
  Shader* GetShader (unsigned idx);
  Shader* CreateShader ();
  /**
   * @}
   */

  /**
   * \name Program handling
   * @{
   */
  bool ContainsProgram (Program* program);
  bool AddProgram (Program* program);
  bool RemoveProgram (Program* program);
  void MoveProgram (Program* program);
  bool SaveProgram (Program* program);


  unsigned GetNumberOfPrograms () const;
  void RemoveProgram (unsigned idx);
  const Program* GetProgram(const QString& name) const;
  Program* GetProgram (const QString& name);
  const Program* GetProgram (unsigned idx) const;
  Program* GetProgram (unsigned idx);
  Program* CreateProgram ();
  /**
   * @}
   */


  /**
   * \name Effect handling
   * @{
   */
  bool ContainsEffect(Effect* effect);
  bool AddEffect (Effect* effect);
  bool RemoveEffect (Effect* effect);
  void MoveEffect (Effect* effect);
  bool SaveEffect (Effect* effect);


  unsigned GetNumberOfEffects () const;
  void RemoveEffect (unsigned idx);
  const Effect* GetEffect(const QString& name) const;
  Effect* GetEffect (const QString& name);
  const Effect* GetEffect (unsigned idx) const;
  Effect* GetEffect (unsigned idx);
  Effect* CreateEffect ();
  /**
   * @}
   */

  /**
   * \name Material handling
   * @{
   */
  bool ContainsMaterial(Material* material);
  bool AddMaterial (Material* material, bool defaultMaterial = false);
  bool RemoveMaterial (Material* material);
  void MoveMaterial (Material* material);
  bool SaveMaterial (Material* material);


  unsigned GetNumberOfMaterials () const;
  void RemoveMaterial (unsigned idx);
  const Material* GetMaterial (const QString& name) const;
  Material* GetMaterial (const QString& name);
  const Material* GetMaterial (unsigned idx) const;
  Material* GetMaterial (unsigned idx);
  Material* CreateMaterial ();
  void SetDefaultMaterial (Material* material);
  Material* GetDefaultMaterial ();
  /**
   * @}
   */

signals:
  void MaterialDataChanged (MaterialData* item);
  void MaterialDataRemoved (MaterialData* item);
  void TextureAdded (Texture* texture);
  void ShaderAdded (Shader* shader);
  void ProgramAdded (Program* program);
  void EffectAdded (Effect* effect);
  void MaterialAdded (Material* material);

};

#endif // MATERIALMANAGER_H
